﻿using zijian666.WorkScheduler.Abstractions;
using zijian666.WorkScheduler.Defines;

namespace zijian666.WorkScheduler.Defaults;

/// <summary>
/// 工作缓冲区
/// </summary>
public class WorkDataBuffer
    : DisposableBase, IDisposable, IWorkDataBuffer
{
    private WorkData[] _data = [];

    /// <summary>
    /// 刷新数据
    /// </summary>
    public void RefreshAll(IEnumerable<WorkData> works)
        => _data = works.OrderBy(x => x.PlanTime).ToArray();

    /// <summary>
    /// 获取工作数量
    /// </summary>
    public int PendingCount => _data.Count(x => x is WorkData { Status: WorkStatus.Pending });

    /// <summary>
    /// 获取并锁定工作
    /// </summary>

    public async Task<WorkData> GetAndLocking(string ticket, IWorkManager manager, CancellationToken cancellation)
    {
        var data = _data;
        var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation, Cancellation);
        for (var i = 0; i < data.Length; i++)
        {
            var work = data[i];
            if (work.IsEmpty)
            {
                continue;
            }
            work = await manager.TryLock(ticket, work, cts.Token);
            if (work.Ticket == ticket) // 锁定成功
            {
                data[i] = default;
                return work;
            }
        }
        return default;
    }

    /// <summary>
    /// 获取并锁定工作
    /// </summary>
    [Obsolete]
    private async Task<WorkData?> GetAndLockingWithRandom(string ticket, IWorkManager manager)
    {
        var data = _data;
        var lastIndex = Array.FindLastIndex(data, x => x.PlanTime <= DateTimeOffset.Now);
        if (lastIndex < 0)
        {
            var next = data.Min(x => x.PlanTime);
            if (next < DateTimeOffset.Now.AddSeconds(30))
            {
                await Task.Delay(next - DateTimeOffset.Now, Cancellation);
                return await GetAndLockingWithRandom(ticket, manager);
            }

            return null;
        }
        foreach (var i in Enumerable.Range(0, lastIndex + 1).OrderBy(x => Guid.NewGuid()))
        {
            var work = data[i];
            if (work.IsEmpty)
            {
                continue;
            }
            work = await manager.TryLock(ticket, work, Cancellation);
            if (work.Ticket == ticket) // 锁定成功
            {
                data[i] = default;
                return work;
            }
        }
        return null;
    }
}
